package applicationModels;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

//import org.uqbar.commons.model.ObservableUtils;
import org.uqbar.commons.utils.Observable;

import resultado.Resultado;
import dominio.Casillero;
import dominio.Fila;
import dominio.Jardin;
import dominio.Mejora;
import dominio.Planta;
import dominio.Usuario;
import dominio.Zombie;
import exceptions.GameOver;

/**
 * Representa el Aplication Model que permite los bindings, tiene ademàs la
 * lógica para hacer pelear plantas y zombies
 * 
 * @author MLinares & MEiroa
 * @category UNQ-TPI UIs 1Q2013
 */
@Observable
public class Juego implements Serializable{
	private static final long serialVersionUID = 1L;
	private List<Planta> plantas;// la totalidad de las plantas
	private List<Zombie> zombies = new ArrayList<Zombie>();// la totalidad de
															// los zombies
	private Usuario usuario; // El usuario
	private Jardin elJardin; 

	private int filaParaAtacar;
	private int filaPlantar;
	private int columnaPlantar;
	private Zombie zombieAtacante;

	private LinkedList<String> mensajesLog = new LinkedList<String>();

	// En la pantalla principal se elige donde plantar
	private Planta plantaPlantar;

	private static Juego juego = null;

	/**
	 * Patron singleton, asegura unicidad del objeto Juego..
	 * 
	 * @return
	 */
	public static synchronized Juego getInstance() {
		if (juego == null) {
			juego = new Juego();
		}

		return juego;
	}

	private Juego() {
		this.initializeProperties();
		this.loadGame();
	}

	public static Juego makeJuego() {
		return (new Juego());
	}

	private void initializeProperties() {
		this.setUsuario(new Usuario());
		this.setElJardin(new Jardin(5, 5));
	}

	private void loadGame() {

		this.loadPlantas();

		this.zombies.add(new Zombie("Zombie feo", 15, 15));
		this.zombies.add(new Zombie("Michael Jackson", 100, 100));
		this.zombies.add(new Zombie("Freddy Kruger", 70, 30));
		this.zombies.add(new Zombie("Frankestein", 25, 50));
		this.zombies.add(new Zombie(" wachiturro", 30, 20));
		this.zombies.add(new Zombie(" HeavyReJodido", 40, 40));

		Planta peas = new Planta("Peashooter", 10, 10, true);
		peas.getMejorasDisponibles().add(
				new Mejora("Mejora 10 defensa", 100, 0, 10));
		peas.getMejorasDisponibles().add(
				new Mejora("Mejora 10 ataque", 100, 10, 0));

		usuario.agregarPlantaZen(peas, false);
		
		for(Planta plant : this.plantas) {
			usuario.agregarPlantaZen(plant, false);
		}

		mensajesLog.add("\n");
		mensajesLog.add("\n");
		mensajesLog.add("\n");
		mensajesLog.add("\n");
		mensajesLog.add("\n");

		// Si hay plantas disponibles inicializar con la primera
		plantaPlantar = this.getPlantasDisponibles().isEmpty() ? null : this
				.getPlantasDisponibles().get(0);
		// Si hay zombies inicializar el atacante con el primero
		zombieAtacante = this.zombies.isEmpty() ? null : this.zombies.get(0);

		filaParaAtacar = this.getNrosDeFila().isEmpty() ? null : this
				.getNrosDeFila().get(0);
	}

	public void loadJardinZen() {
		usuario.getJardinZen().reiniciar();

		// Agregar una planta terrestre para empezar el juego
		Planta peas = new Planta("Peashooter", 10, 10, true);

		usuario.agregarPlantaZen(peas);
		usuario.setPuntos(500);
		//ObservableUtils.forceFirePropertyChanged(this, "plantasDisponibles",this.getPlantasDisponibles());
	}

	public void loadPlantas() {
		this.setPlantas(new ArrayList<Planta>());

		Planta sun = new Planta("Sunflower", 15, 15, true);

		Planta cherry = new Planta("Cherry", 50, 5, true);

		Planta superPea = new Planta("Fume", 30, 30, true);
		superPea.getMejorasDisponibles().add(
				new Mejora("Mejora 500 defensa", 250, 0, 500));
		superPea.getMejorasDisponibles().add(
				new Mejora("Mejora 500 ataque", 250, 500, 0));

		this.plantas.add(sun);
		this.plantas.add(cherry);
		this.plantas.add(superPea);
		this.plantas.add(new Planta("Pumpkin", 1, 70, true));
		this.plantas.add(new Planta("Wall Nut", 1, 60, false));
		this.plantas.add(new Planta("Tangle Kelp", 45, 10, false));
		this.plantas.add(new Planta("Cat Tail", 40, 30, false));
		this.plantas.add(new Planta("AquaShooter", 15, 15, false));
	}

	
	public void putMessage(String msg) {
		String msgEdit = msg + "\n";
		this.mensajesLog.addFirst(msgEdit);
		this.updateMsgs();
	}

	public void updateMsgs() {
		//ObservableUtils.forceFirePropertyChanged(this, "mensajesLog",
				this.getMensajesLog();
	}

	/**
	 * Desplanta todo el jardin Resetea el jardinZen Resetea todas las plantas
	 */
	public void reiniciar() {
		// remueve todas las plantas
		this.getElJardin().reiniciar();

		// reinicia el jardin zen y agrega las primeras de vuelta
		this.loadJardinZen();

		// cargar la totalidad de las plantas nuevamente
		this.loadPlantas();
		this.putMessage("<<Se ha reiniciado el juego!>>");
		this.plantaPlantar = this.getUsuario().getJardinZen()
				.getPlanta("peashooters");
		//ObservableUtils.forceFirePropertyChanged(this, "plantaPlantar",				this.getPlantaPlantar());
		//ObservableUtils.forceFirePropertyChanged(this, "puntaje",				this.getPuntaje());
	}

	/**
	 * Inicia la pelea del zombie contra una fila de plantas y notifica al
	 * usuario del resultado
	 * 
	 * @param zombie
	 * @param fila
	 */
	public void pelear() {
		Zombie atacante = this.zombieAtacante.makeClone();
		Resultado resultado = atacante.atacarFila(this
				.obtenerFila(this.filaParaAtacar));

		try {
			resultado.notificar(this.usuario, this.plantas);
			//ObservableUtils.forceFirePropertyChanged(this,"plantasDisponibles", this.getPlantasDisponibles());

			this.putMessage(resultado.getMensajeResultado());
			//System.out.println(this.mensajesLog);
		} catch (GameOver gameOver) {
			this.putMessage(resultado.getMensajeResultado());
			//System.out.println(this.mensajesLog);
		}

	}

	public Fila obtenerFila(int numero) {
		return (this.elJardin.getFilas().get(numero - 1));
	}

	/**
	 * Retorna la lista de numeros de filas disponibles en el jardin
	 * 
	 * @return
	 */
	public List<Integer> getNrosDeFila() {
		int len = this.getElJardin().getHeight();

		ArrayList<Integer> ret = new ArrayList<Integer>(len);

		for (int i = 1; i <= len; i++) {
			ret.add(i);
		}

		return ret;
	}

	public List<Planta> getPlantasDisponibles() {
		List<Planta> plantasDisponibles = new ArrayList<Planta>();

		for (Planta p : this.usuario.getJardinZen().getJardinTerrestre()) {
			plantasDisponibles.add(p);
		}

		for (Planta p : this.usuario.getJardinZen().getJardinAcuatico()) {
			plantasDisponibles.add(p);
		}

		return plantasDisponibles;
	}

	/**
	 * String con la coordenada del casillero especificado
	 * 
	 * @param casillero
	 * @return (fila, columna) del casillero
	 */
	public String getCoordCasillero(Casillero casillero) {
		int filas = this.getElJardin().getHeight();
		int casilleros = this.getElJardin().getWidth();

		for (int i = 0; i < filas; i++) {
			for (int j = 0; j < casilleros; j++) {

				Casillero actual = this.getElJardin().getFilas().get(i)
						.getCasilleros().get(j);

				if (casillero.equals(actual)) {
					return "[" + (i + 1) + ", " + (j + 1) + "]";
				}
			}
		}

		return "";
	}

	public Casillero getCasillero(int fila, int columna) {
		return this.elJardin.getFilas().get(fila).getCasilleros().get(columna);
	}

	public Integer getPuntaje() {
		return this.getUsuario().getPuntos();
	}

	public Planta getPlantaPlantar() {
		return plantaPlantar;
	}

	public void setPlantaPlantar(Planta plantaAPlantar) {
		this.plantaPlantar = plantaAPlantar;
	}

	public Zombie getZombieAtacante() {
		return zombieAtacante;
	}

	public void setZombieAtacante(Zombie zombieAtacante) {
		this.zombieAtacante = zombieAtacante;
	}

	public Jardin getElJardin() {
		return elJardin;
	}

	public void setElJardin(Jardin elJardin) {
		this.elJardin = elJardin;
	}

	public List<Planta> getPlantas() {
		return plantas;
	}

	public void setPlantas(List<Planta> plantas) {
		this.plantas = plantas;
	}

	public List<Zombie> getZombies() {
		return zombies;
	}

	public void setZombies(List<Zombie> zombies) {
		this.zombies = zombies;
	}

	public Usuario getUsuario() {
		return usuario;
	}

	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}

	public LinkedList<String> getMensajesLog() {
		return mensajesLog;
	}

	public void setMensajesLog(LinkedList<String> mensajesLog) {
		this.mensajesLog = mensajesLog;
	}

	public int getFilaParaAtacar() {
		return filaParaAtacar;
	}

	public void setFilaParaAtacar(int filaParaAtacar) {
		this.filaParaAtacar = filaParaAtacar;
	}

	public List<Fila> getFilas() {
		return this.elJardin.getFilas();
	}

	public Integer getFilaPlantar() {
		return filaPlantar;
	}

	public void setFilaPlantar(int filaPlantar) {
		this.filaPlantar = filaPlantar;
	}

	public Integer getColumnaPlantar() {
		return columnaPlantar;
	}

	public void setColumnaPlantar(int columnaPlantar) {
		this.columnaPlantar = columnaPlantar;
	}
   
}
